//! TTL (Time To Live) support for Redis-rs2
//! 
//! This module provides expiration time support for keys in the storage system.
//! It manages automatic cleanup of expired keys and provides TTL-related operations.

// use crate::error::Result;  // 已注释：未使用的导入
use std::collections::HashMap;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use tokio::time::interval;
use tracing::{debug, info};

/// TTL manager that handles key expiration
#[derive(Debug)]
pub struct TtlManager {
    /// Map of key to expiration timestamp (seconds since UNIX epoch)
    expirations: HashMap<String, u64>,
    /// Map of key to expiration instant (for internal use)
    expiration_instants: HashMap<String, Instant>,
}

impl TtlManager {
    /// Create a new TTL manager
    pub fn new() -> Self {
        Self {
            expirations: HashMap::new(),
            expiration_instants: HashMap::new(),
        }
    }
    
    /// Set expiration time for a key (in seconds from now)
    pub fn expire(&mut self, key: String, seconds: u64) -> bool {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        
        let expiration_time = now + seconds;
        let expiration_instant = Instant::now() + Duration::from_secs(seconds);
        
        debug!("Set expiration for key '{}' in {} seconds", key, seconds);
        
        self.expirations.insert(key.clone(), expiration_time);
        self.expiration_instants.insert(key, expiration_instant);
        
        true
    }
    
    /// Set expiration time for a key at a specific timestamp
    pub fn expire_at(&mut self, key: String, timestamp: u64) -> bool {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        
        if timestamp <= now {
            // Already expired
            return false;
        }
        
        let duration_from_now = Duration::from_secs(timestamp - now);
        let expiration_instant = Instant::now() + duration_from_now;
        
        debug!("Set expiration for key '{}' at timestamp {}", key, timestamp);
        
        self.expirations.insert(key.clone(), timestamp);
        self.expiration_instants.insert(key, expiration_instant);
        
        true
    }
    
    /// Remove expiration for a key (make it persistent)
    pub fn persist(&mut self, key: &str) -> bool {
        let had_expiration = self.expirations.contains_key(key);
        self.expirations.remove(key);
        self.expiration_instants.remove(key);
        
        if had_expiration {
            debug!("Removed expiration for key '{}'", key);
        }
        
        had_expiration
    }
    
    /// Get TTL for a key in seconds (-1 if no expiration, -2 if key doesn't exist)
    pub fn ttl(&self, key: &str) -> i64 {
        match self.expirations.get(key) {
            Some(&expiration_time) => {
                let now = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_secs();
                
                if expiration_time > now {
                    (expiration_time - now) as i64
                } else {
                    // Key has expired but hasn't been cleaned up yet
                    0
                }
            }
            None => -1, // No expiration set
        }
    }
    
    /// Get TTL for a key in milliseconds
    pub fn pttl(&self, key: &str) -> i64 {
        match self.expiration_instants.get(key) {
            Some(&expiration_instant) => {
                let now = Instant::now();
                
                if expiration_instant > now {
                    let remaining = expiration_instant.duration_since(now);
                    remaining.as_millis() as i64
                } else {
                    // Key has expired but hasn't been cleaned up yet
                    0
                }
            }
            None => -1, // No expiration set
        }
    }
    
    /// Check if a key has expired
    pub fn is_expired(&self, key: &str) -> bool {
        match self.expirations.get(key) {
            Some(&expiration_time) => {
                let now = SystemTime::now()
                    .duration_since(UNIX_EPOCH)
                    .unwrap()
                    .as_secs();
                
                expiration_time <= now
            }
            None => false, // No expiration means not expired
        }
    }
    
    /// Get all expired keys
    pub fn get_expired_keys(&self) -> Vec<String> {
        let now = SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs();
        
        self.expirations
            .iter()
            .filter(|(_, expiration_time)| **expiration_time <= now)
            .map(|(key, _)| key.clone())
            .collect()
    }
    
    /// Remove expired keys from TTL tracking
    pub fn cleanup_expired_keys(&mut self, expired_keys: &[String]) {
        for key in expired_keys {
            self.expirations.remove(key);
            self.expiration_instants.remove(key);
            debug!("Cleaned up expired key: {}", key);
        }
    }
    
    /// Remove a key from TTL tracking (when key is deleted)
    pub fn remove_key(&mut self, key: &str) {
        self.expirations.remove(key);
        self.expiration_instants.remove(key);
    }
    
    /// Get the number of keys with expiration set
    pub fn len(&self) -> usize {
        self.expirations.len()
    }
    
    /// Check if TTL manager is empty
    pub fn is_empty(&self) -> bool {
        self.expirations.is_empty()
    }
    
    /// Clear all TTL data
    pub fn clear(&mut self) {
        self.expirations.clear();
        self.expiration_instants.clear();
        info!("TTL manager cleared");
    }
}

impl Default for TtlManager {
    fn default() -> Self {
        Self::new()
    }
}

/// Background task for cleaning up expired keys
pub struct TtlCleanupTask {
    /// How often to run cleanup (in seconds)
    cleanup_interval: Duration,
}

impl TtlCleanupTask {
    /// Create a new cleanup task
    pub fn new(cleanup_interval_seconds: u64) -> Self {
        Self {
            cleanup_interval: Duration::from_secs(cleanup_interval_seconds),
        }
    }
    
    /// Start the background cleanup task
    pub async fn start<F>(&self, mut cleanup_callback: F)
    where
        F: FnMut() -> Vec<String> + Send + 'static,
    {
        let mut interval_timer = interval(self.cleanup_interval);
        
        loop {
            interval_timer.tick().await;
            
            let expired_keys = cleanup_callback();
            
            if !expired_keys.is_empty() {
                info!("Cleaned up {} expired keys", expired_keys.len());
            }
        }
    }
}

/// Utility functions for TTL operations
pub mod ttl_utils {
    use super::*;
    
    /// Get current Unix timestamp in seconds
    pub fn current_timestamp() -> u64 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_secs()
    }
    
    /// Get current Unix timestamp in milliseconds
    pub fn current_timestamp_millis() -> u64 {
        SystemTime::now()
            .duration_since(UNIX_EPOCH)
            .unwrap()
            .as_millis() as u64
    }
    
    /// Convert seconds to milliseconds
    pub fn seconds_to_millis(seconds: u64) -> u64 {
        seconds * 1000
    }
    
    /// Convert milliseconds to seconds
    pub fn millis_to_seconds(millis: u64) -> u64 {
        millis / 1000
    }
    
    /// Check if a timestamp represents a time in the past
    pub fn is_past_timestamp(timestamp: u64) -> bool {
        timestamp <= current_timestamp()
    }
    
    /// Calculate duration until a timestamp
    pub fn duration_until_timestamp(timestamp: u64) -> Option<Duration> {
        let now = current_timestamp();
        if timestamp > now {
            Some(Duration::from_secs(timestamp - now))
        } else {
            None
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_ttl_manager_basic() {
        let mut ttl_manager = TtlManager::new();
        
        assert!(ttl_manager.is_empty());
        assert_eq!(ttl_manager.len(), 0);
        
        // Test setting expiration
        assert!(ttl_manager.expire("key1".to_string(), 10));
        assert_eq!(ttl_manager.len(), 1);
        assert!(!ttl_manager.is_empty());
        
        // Test TTL retrieval
        let ttl = ttl_manager.ttl("key1");
        assert!(ttl > 0 && ttl <= 10);
        
        // Test non-existent key
        assert_eq!(ttl_manager.ttl("nonexistent"), -1);
        
        // Test persistence
        assert!(ttl_manager.persist("key1"));
        assert_eq!(ttl_manager.ttl("key1"), -1);
        assert_eq!(ttl_manager.len(), 0);
    }
    
    #[tokio::test]
    async fn test_ttl_expiration() {
        let mut ttl_manager = TtlManager::new();
        
        // Set a key to expire in 1 second
        ttl_manager.expire("test_key".to_string(), 1);
        
        // Key should not be expired yet
        assert!(!ttl_manager.is_expired("test_key"));
        assert!(ttl_manager.ttl("test_key") > 0);
        
        // Wait for expiration (in real test we'd use time mocking)
        tokio::time::sleep(std::time::Duration::from_millis(1100)).await;
        
        // Key should now be expired
        assert!(ttl_manager.is_expired("test_key"));
        assert_eq!(ttl_manager.ttl("test_key"), 0);
        
        // Get expired keys
        let expired_keys = ttl_manager.get_expired_keys();
        assert_eq!(expired_keys.len(), 1);
        assert_eq!(expired_keys[0], "test_key");
        
        // Cleanup expired keys
        ttl_manager.cleanup_expired_keys(&expired_keys);
        assert_eq!(ttl_manager.len(), 0);
    }
    
    #[test]
    fn test_ttl_expire_at() {
        let mut ttl_manager = TtlManager::new();
        
        let now = ttl_utils::current_timestamp();
        let future_timestamp = now + 10;
        let past_timestamp = now - 10;
        
        // Test setting expiration at future timestamp
        assert!(ttl_manager.expire_at("key1".to_string(), future_timestamp));
        assert!(ttl_manager.ttl("key1") > 0);
        
        // Test setting expiration at past timestamp (should fail)
        assert!(!ttl_manager.expire_at("key2".to_string(), past_timestamp));
        assert_eq!(ttl_manager.ttl("key2"), -1);
    }
    
    #[test]
    fn test_ttl_utils() {
        let now = ttl_utils::current_timestamp();
        let now_millis = ttl_utils::current_timestamp_millis();
        
        assert!(now > 0);
        assert!(now_millis > 0);
        assert!(now_millis >= now * 1000);
        
        // Test conversions
        assert_eq!(ttl_utils::seconds_to_millis(5), 5000);
        assert_eq!(ttl_utils::millis_to_seconds(5000), 5);
        
        // Test past timestamp check
        assert!(ttl_utils::is_past_timestamp(now - 10));
        assert!(!ttl_utils::is_past_timestamp(now + 10));
        
        // Test duration calculation
        let future_time = now + 10;
        let duration = ttl_utils::duration_until_timestamp(future_time);
        assert!(duration.is_some());
        assert!(duration.unwrap().as_secs() <= 10);
        
        let past_time = now - 10;
        let duration = ttl_utils::duration_until_timestamp(past_time);
        assert!(duration.is_none());
    }
}